#include <stdio.h>
#include <string.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

#include "main.h"
#include "config.debug.h"
#include "db.h"

#include "builtin/builtins.h"
#include "builtin/blog_utils.h"

#ifdef LIBXML_TREE_ENABLED
#define WPXML_TITLE "title"
#define WPXML_LINK "link"
#define WPXML_CONTENT_NS "content"
#define WPXML_CONTENT "encoded"
#define WPXML_NS "wp"
#define WPXML_STATUS "status"
#define WPXML_STATUS_PUBL "publish"
#define WPXML_DATE "post_date"
#define WPXML_CATG "category"
#define WPXML_CATG_DM "domain"
#define WPXML_CATG_NM "nicename"
#define WPXML_CATG_CAT WPXML_CATG
#define WPXML_CATG_PST "post_tag"

static void *db_handle = NULL;
static size_t entries = 0, entries_added = 0, entries_publ = 0;


static void
cvt_close_db(void)
{
  db_close(db_handle);
  db_shutdown();
}

static void
cvt_init_db(void **hndl, const char *path)
{
  assert( path == NULL );
  if (db_set_arg(path) != DB_ERR_OK) {
    E("Error setting db argument (%s)..", path);
    goto error;
  }
  if (db_preinit() != DB_ERR_OK) {
    E2("Error init db..");
    goto error;
  }
  if (db_init("blog", hndl) != DB_ERR_OK) {
    E2("Error during db init..");
    goto error;
  }
  return;
error:
  exit(EXIT_FAILURE);
}

static string_t *
prepare_link(const char *raw_link)
{
  string_t *result = create_string(raw_link);
  int idx = 0;
  
  if ( (idx = string_cmp(result, "://", 0)) >= 0 ) idx += 3;
  if ( (idx = string_cmp(result, "/", idx)) >= 0 ) {
    del_string(result, 0, idx+1);
  }

  char *tmp = (char *) get_string(result);
  size_t len = get_strlen(result);
  if ( tmp[len - 1] == '/' )
    del_string(result, len-1, 1);
  return (result);
}

static void
parse_xml(xmlNode * a_node)
{
  xmlNode *cur_node = NULL;
  xmlChar *title = NULL, *link = NULL, *content = NULL, *xml_published = NULL, *xml_date = NULL, *xml_catg = NULL, *xml_tag = NULL, *xml_tmp = NULL;
  bool published = false;
  const xmlChar *content_ns = NULL;
  string_t *catg = create_empty_string(), *conv_link = NULL;

  for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
    if (cur_node->type == XML_ELEMENT_NODE) {
      if ( xmlStrEqual(cur_node->name, WPXML_TITLE) == 1 ) {
        title = xmlNodeGetContent(cur_node);
      } else if ( xmlStrEqual(cur_node->name, WPXML_LINK) == 1 ) {
        link = xmlNodeGetContent(cur_node);
        if (link) conv_link = prepare_link(link);
          else fprintf(stderr, "Missing link ..\n");
      } else if ( xmlStrEqual(cur_node->name, WPXML_CATG) == 1 ) {
        xml_tmp = xmlNodeGetContent(cur_node);
        if ((xml_catg = xmlGetProp(cur_node, WPXML_CATG_DM)) != NULL) {
          if ( xmlStrEqual(xml_catg, WPXML_CATG_CAT) == 1) {
            xml_tag = xmlGetProp(cur_node, WPXML_CATG_NM);
            if (xml_tag) {
              set_string(catg, xml_tag);
              if (db_handle) blog_add_catg(db_handle, xml_tmp, xml_tag);
            } else {
              fprintf(stderr, "Error while parsing category ..\n");
            } 
            xmlFree(xml_tag);
          } else if ( xmlStrEqual(xml_catg, WPXML_CATG_PST) == 1) {
            if ((xml_tag = xmlGetProp(cur_node, WPXML_CATG_NM)) != NULL) {
              blog_add_tag(db_handle, xml_tag, xml_tmp);
              if (conv_link) {
                blog_add_tagpost(db_handle, get_string(conv_link), xml_tag);
              }
              xmlFree(xml_tag);
            }
          } else fprintf(stderr, "Unknown category domain: %s\n", xml_catg);
          xmlFree(xml_catg);
        } else fprintf(stderr, "Category found without content ..\n");
        xmlFree(xml_tmp);
      } else if ( cur_node->ns != NULL ) {
        if ( xmlStrEqual(cur_node->ns->prefix, WPXML_CONTENT_NS) == 1 ) {
          if ( xmlStrEqual(cur_node->name, WPXML_CONTENT) == 1 ) {
            if (cur_node->type == XML_ELEMENT_NODE) {
              content_ns = cur_node->ns->prefix;
              content = xmlNodeGetContent(cur_node);
            }
          }
        }
        if ( xmlStrEqual(cur_node->ns->prefix, WPXML_NS) == 1 ) {
          if ( xmlStrEqual(cur_node->name, WPXML_STATUS) == 1 ) {
            if (cur_node->type == XML_ELEMENT_NODE) {
              xml_published = xmlNodeGetContent(cur_node);
              if (strcmp(xml_published, WPXML_STATUS_PUBL) == 0) {
                published = true;
              }
            }
          } else if ( xmlStrEqual(cur_node->name, WPXML_DATE) == 1 ) {
            xml_date = xmlNodeGetContent(cur_node);
          }
        }
      }

      xmlNsPtr ns = cur_node->nsDef;
      printf("#");
      while(ns != NULL) {
        //printf("\nNamespace definition: %s (%s)\n", ns->href, ns->prefix);
        ns = ns->next;
      }
    } else printf(".");
    parse_xml(cur_node->children);
  }
  if (title && link && content && content_ns) {
    entries++;
    printf(".");
    if (db_handle) {
      if (blog_new_blog_entry_sd(db_handle, get_string(conv_link), title, content, "admin", get_string(catg), xml_date) == 0) {
        entries_added++;
      }
      if (blog_upublish_link(db_handle, published, get_string(conv_link)) == 0) {
        entries_publ++;
      }
    }
  }
  xmlFree(title);
  xmlFree(link);
  xmlFree(content);
  xmlFree(xml_published);
  xmlFree(xml_date);
  free_string(catg);
  free_string(conv_link);
}

int
process_xml(char *path)
{
  xmlDoc *doc = NULL;
  xmlNode *root_element = NULL;

  printf("\t'.' - XML node does not contain useful information\n");
  printf("\t'#' - Found useful content\n");
  /*parse the file and get the DOM */
  doc = xmlReadFile(path, NULL, 0);
  if (doc == NULL) {
    printf("error: could not parse file %s\n", path);
    return (1);
  }
  /*Get the root element node */
  root_element = xmlDocGetRootElement(doc);
  parse_xml(root_element);
  /*free the document */
  xmlFreeDoc(doc);
  xmlCleanupParser();
  return (0);
}

void
print_status(void) {
  printf("done\nEntries found: %lu\n", (long unsigned int)entries);
  printf("Entries added: %lu/%lu\n", (long unsigned int)entries_added, (long unsigned int)entries);
  printf("Entries published: %lu/%lu\n", (long unsigned int)entries_publ, (long unsigned int)entries_added);
}

int
main(int argc, char **argv)
{
  int retval = EXIT_SUCCESS;

  fprintf(stderr, "*** WORDPRESS XML CONVERTER ***\n");
#ifdef NC_VERSION
  fprintf(stderr, "VERSION: %s\n", NC_VERSION);
#endif
  LIBXML_TEST_VERSION

  if (argc == 2) {
    fprintf(stderr, "Parsing file %s, please wait ..\n", argv[1]);
    retval = process_xml(argv[1]);
    print_status();
    return (retval);
  } else if (argc != 3) {
    fprintf(stderr, "%s Wordpress_XML_file [DB_ROOT]\n", argv[0]);
    return(EXIT_FAILURE);
  }

  cvt_init_db(&db_handle, argv[2]);
  if (blog_create_tables() != DB_ERR_OK) {
    fprintf(stderr, "blog: table check failed\n");
    retval = EXIT_FAILURE;
    goto error;
  }
  fprintf(stderr, "Parsing file %s and importing results into the databse, please wait ..\n", argv[1]);
  process_xml(argv[1]);
  print_status();
error:
  cvt_close_db();
  return (retval);
}
#else
int main(void) {
  fprintf(stderr, "libxml: Tree support not compiled in\n");
  exit(1);
}
#endif

